ಪೈಥಾನ್ನ unittest.mock ಲೈಬ್ರರಿಯನ್ನು ಕರಗತ ಮಾಡಿಕೊಳ್ಳಿ. ದೃಢವಾದ, ಪ್ರತ್ಯೇಕವಾದ ಯೂನಿಟ್ ಪರೀಕ್ಷೆಗಾಗಿ ಟೆಸ್ಟ್ ಡಬಲ್ಸ್, ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ಗಳು, ಸ್ಟಬ್ಗಳು, ಫೇಕ್ಗಳು ಮತ್ತು ಪ್ಯಾಚ್ ಡೆಕೋರೇಟರ್ ಕುರಿತು ಆಳವಾದ ವಿಶ್ಲೇಷಣೆ.
ಪೈಥಾನ್ ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ಗಳು: ಟೆಸ್ಟ್ ಡಬಲ್ ಅನುಷ್ಠಾನಕ್ಕೆ ಒಂದು ಸಮಗ್ರ ಮಾರ್ಗದರ್ಶಿ
ಆಧುನಿಕ ಸಾಫ್ಟ್ವೇರ್ ಅಭಿವೃದ್ಧಿಯ ಜಗತ್ತಿನಲ್ಲಿ, ಕೋಡ್ ಬರೆಯುವುದು ಅರ್ಧ ಯುದ್ಧ ಮಾತ್ರ. ಆ ಕೋಡ್ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿದೆ, ದೃಢವಾಗಿದೆ ಮತ್ತು ನಿರೀಕ್ಷಿತವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳುವುದು ಇನ್ನೊಂದು, ಅಷ್ಟೇ ನಿರ್ಣಾಯಕ ಅರ್ಧಭಾಗವಾಗಿದೆ. ಇಲ್ಲಿ ಸ್ವಯಂಚಾಲಿತ ಪರೀಕ್ಷೆಗಳು ಬರುತ್ತವೆ. ನಿರ್ದಿಷ್ಟವಾಗಿ, ಯೂನಿಟ್ ಪರೀಕ್ಷೆಯು ಒಂದು ಮೂಲಭೂತ ಅಭ್ಯಾಸವಾಗಿದ್ದು, ಇದು ಅಪ್ಲಿಕೇಶನ್ನ ಪ್ರತ್ಯೇಕ ಘಟಕಗಳು ಅಥವಾ 'ಯೂನಿಟ್ಗಳನ್ನು' ಪ್ರತ್ಯೇಕವಾಗಿ ಪರೀಕ್ಷಿಸುವುದನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ. ಆದಾಗ್ಯೂ, ಈ ಪ್ರತ್ಯೇಕತೆಯನ್ನು ಹೇಳುವುದು ಸುಲಭ, ಮಾಡುವುದು ಕಷ್ಟ. ನೈಜ-ಪ್ರಪಂಚದ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಪರಸ್ಪರ ಸಂಪರ್ಕ ಹೊಂದಿದ ಆಬ್ಜೆಕ್ಟ್ಗಳು, ಸೇವೆಗಳು ಮತ್ತು ಬಾಹ್ಯ ಸಿಸ್ಟಮ್ಗಳ ಸಂಕೀರ್ಣ ಜಾಲಗಳಾಗಿವೆ. ಒಂದು ಕಾರ್ಯವು ಡೇಟಾಬೇಸ್, ಮೂರನೇ-ಪಕ್ಷದ API, ಅಥವಾ ನಿಮ್ಮ ಸಿಸ್ಟಮ್ನ ಇನ್ನೊಂದು ಸಂಕೀರ್ಣ ಭಾಗವನ್ನು ಅವಲಂಬಿಸಿದ್ದರೆ ಅದನ್ನು ನೀವು ಹೇಗೆ ಪರೀಕ್ಷಿಸಬಹುದು?
ಉತ್ತರವು ಒಂದು ಶಕ್ತಿಶಾಲಿ ತಂತ್ರದಲ್ಲಿ ಅಡಗಿದೆ: ಟೆಸ್ಟ್ ಡಬಲ್ಸ್ ಅನ್ನು ಬಳಸುವುದು. ಮತ್ತು ಪೈಥಾನ್ ಪರಿಸರ ವ್ಯವಸ್ಥೆಯಲ್ಲಿ, ಅವುಗಳನ್ನು ರಚಿಸಲು ಪ್ರಾಥಮಿಕ ಸಾಧನವು ಬಹುಮುಖಿ ಮತ್ತು ಅನಿವಾರ್ಯವಾದ unittest.mock ಲೈಬ್ರರಿಯಾಗಿದೆ. ಈ ಮಾರ್ಗದರ್ಶಿಯು ನಿಮ್ಮನ್ನು ಪೈಥಾನ್ನಲ್ಲಿರುವ ಮಾಕ್ಗಳು ಮತ್ತು ಟೆಸ್ಟ್ ಡಬಲ್ಗಳ ಜಗತ್ತಿಗೆ ಆಳವಾಗಿ ಕೊಂಡೊಯ್ಯುತ್ತದೆ. ನಾವು ಅವುಗಳ ಹಿಂದಿನ 'ಏಕೆ' ಅನ್ನು ಅನ್ವೇಷಿಸುತ್ತೇವೆ, ವಿಭಿನ್ನ ಪ್ರಕಾರಗಳನ್ನು ಸ್ಪಷ್ಟಪಡಿಸುತ್ತೇವೆ ಮತ್ತು ಸ್ವಚ್ಛವಾದ, ವೇಗವಾದ ಮತ್ತು ಹೆಚ್ಚು ಪರಿಣಾಮಕಾರಿ ಪರೀಕ್ಷೆಗಳನ್ನು ಬರೆಯಲು ನಿಮಗೆ ಸಹಾಯ ಮಾಡಲು unittest.mock ಅನ್ನು ಬಳಸಿಕೊಂಡು ಪ್ರಾಯೋಗಿಕ, ನೈಜ-ಪ್ರಪಂಚದ ಉದಾಹರಣೆಗಳನ್ನು ಒದಗಿಸುತ್ತೇವೆ.
ಟೆಸ್ಟ್ ಡಬಲ್ಗಳು ಯಾವುವು ಮತ್ತು ಅವು ನಮಗೆ ಏಕೆ ಬೇಕು?
ನಿಮ್ಮ ಕಂಪನಿಯ ಡೇಟಾಬೇಸ್ನಿಂದ ಬಳಕೆದಾರರ ಪ್ರೊಫೈಲ್ ಅನ್ನು ಹಿಂಪಡೆದು ಅದನ್ನು ಫಾರ್ಮ್ಯಾಟ್ ಮಾಡುವ ಕಾರ್ಯವನ್ನು ನೀವು ನಿರ್ಮಿಸುತ್ತಿದ್ದೀರಿ ಎಂದು ಊಹಿಸಿ. ಕಾರ್ಯದ ಸಹಿ ಹೀಗಿರಬಹುದು: get_formatted_user_profile(user_id, db_connection).
ಈ ಕಾರ್ಯವನ್ನು ಪರೀಕ್ಷಿಸಲು, ನೀವು ಹಲವಾರು ಸವಾಲುಗಳನ್ನು ಎದುರಿಸುತ್ತೀರಿ:
- ಲೈವ್ ಸಿಸ್ಟಮ್ನ ಮೇಲೆ ಅವಲಂಬನೆ: ನಿಮ್ಮ ಪರೀಕ್ಷೆಗೆ ಚಾಲನೆಯಲ್ಲಿರುವ ಡೇಟಾಬೇಸ್ ಅಗತ್ಯವಿರುತ್ತದೆ. ಇದು ಪರೀಕ್ಷೆಗಳನ್ನು ನಿಧಾನಗೊಳಿಸುತ್ತದೆ, ಹೊಂದಿಸಲು ಸಂಕೀರ್ಣಗೊಳಿಸುತ್ತದೆ ಮತ್ತು ಬಾಹ್ಯ ಸಿಸ್ಟಮ್ನ ಸ್ಥಿತಿ ಮತ್ತು ಲಭ್ಯತೆಯ ಮೇಲೆ ಅವಲಂಬಿತವಾಗಿರುತ್ತದೆ.
- ಮುನ್ಸೂಚಿಸಲಾಗದಿರುವಿಕೆ: ಡೇಟಾಬೇಸ್ನಲ್ಲಿನ ಡೇಟಾ ಬದಲಾಗಬಹುದು, ನಿಮ್ಮ ಫಾರ್ಮ್ಯಾಟಿಂಗ್ ತರ್ಕವು ಸರಿಯಾಗಿದ್ದರೂ ಸಹ ನಿಮ್ಮ ಪರೀಕ್ಷೆಯು ವಿಫಲಗೊಳ್ಳಲು ಕಾರಣವಾಗಬಹುದು. ಇದು ಪರೀಕ್ಷೆಗಳನ್ನು 'ಫ್ಲಾಕಿ' ಅಥವಾ ನಿರ್ಧಾರಕವಲ್ಲದಂತೆ ಮಾಡುತ್ತದೆ.
- ಎಡ್ಜ್ ಕೇಸ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸುವಲ್ಲಿ ತೊಂದರೆ: ಡೇಟಾಬೇಸ್ ಸಂಪರ್ಕ ವಿಫಲವಾದರೆ ಏನಾಗುತ್ತದೆ, ಅಥವಾ ಅದು ಕೆಲವು ಡೇಟಾ ಇಲ್ಲದ ಬಳಕೆದಾರರನ್ನು ಹಿಂತಿರುಗಿಸಿದರೆ ಏನಾಗುತ್ತದೆ ಎಂಬುದನ್ನು ನೀವು ಹೇಗೆ ಪರೀಕ್ಷಿಸುತ್ತೀರಿ? ನೈಜ ಡೇಟಾಬೇಸ್ನೊಂದಿಗೆ ಈ ನಿರ್ದಿಷ್ಟ ಸನ್ನಿವೇಶಗಳನ್ನು ಅನುಕರಿಸುವುದು ನಂಬಲಾಗದಷ್ಟು ಕಷ್ಟಕರವಾಗಿರುತ್ತದೆ.
ಒಂದು ಟೆಸ್ಟ್ ಡಬಲ್ ಎಂದರೆ ಪರೀಕ್ಷೆಯ ಸಮಯದಲ್ಲಿ ನೈಜ ಆಬ್ಜೆಕ್ಟ್ಗೆ ಬದಲಾಗಿ ನಿಲ್ಲುವ ಯಾವುದೇ ಆಬ್ಜೆಕ್ಟ್ಗೆ ಒಂದು ಸಾಮಾನ್ಯ ಪದವಾಗಿದೆ. ನೈಜ db_connection ಅನ್ನು ಟೆಸ್ಟ್ ಡಬಲ್ನೊಂದಿಗೆ ಬದಲಾಯಿಸುವ ಮೂಲಕ, ನಾವು ನಿಜವಾದ ಡೇಟಾಬೇಸ್ನ ಮೇಲಿನ ಅವಲಂಬನೆಯನ್ನು ಕಡಿತಗೊಳಿಸಬಹುದು ಮತ್ತು ಪರೀಕ್ಷಾ ಪರಿಸರದ ಮೇಲೆ ಸಂಪೂರ್ಣ ನಿಯಂತ್ರಣವನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು.
ಟೆಸ್ಟ್ ಡಬಲ್ಗಳನ್ನು ಬಳಸುವುದರಿಂದ ಹಲವಾರು ಪ್ರಮುಖ ಪ್ರಯೋಜನಗಳಿವೆ:
- ಪ್ರತ್ಯೇಕತೆ: ಅವು ನಿಮ್ಮ ಕೋಡ್ ಘಟಕವನ್ನು (ಉದಾ., ಫಾರ್ಮ್ಯಾಟಿಂಗ್ ತರ್ಕ) ಅದರ ಅವಲಂಬನೆಗಳಿಂದ (ಉದಾ., ಡೇಟಾಬೇಸ್) ಸಂಪೂರ್ಣ ಪ್ರತ್ಯೇಕವಾಗಿ ಪರೀಕ್ಷಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಪರೀಕ್ಷೆ ವಿಫಲವಾದರೆ, ಸಮಸ್ಯೆ ಪರೀಕ್ಷೆಯಲ್ಲಿರುವ ಘಟಕದಲ್ಲಿದೆ, ಬೇರೆಲ್ಲಿಯೂ ಇಲ್ಲ ಎಂದು ನಿಮಗೆ ತಿಳಿದಿರುತ್ತದೆ.
- ವೇಗ: ನೆಟ್ವರ್ಕ್ ವಿನಂತಿಗಳು ಅಥವಾ ಡೇಟಾಬೇಸ್ ಪ್ರಶ್ನೆಗಳಂತಹ ನಿಧಾನ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ಇನ್-ಮೆಮೊರಿ ಟೆಸ್ಟ್ ಡಬಲ್ನೊಂದಿಗೆ ಬದಲಾಯಿಸುವುದರಿಂದ ನಿಮ್ಮ ಪರೀಕ್ಷಾ ಸೂಟ್ ನಾಟಕೀಯವಾಗಿ ವೇಗವಾಗಿ ರನ್ ಆಗುತ್ತದೆ. ವೇಗದ ಪರೀಕ್ಷೆಗಳನ್ನು ಹೆಚ್ಚಾಗಿ ನಡೆಸಲಾಗುತ್ತದೆ, ಇದು ಡೆವಲಪರ್ಗಳಿಗೆ ಬಿಗಿಯಾದ ಪ್ರತಿಕ್ರಿಯೆ ಲೂಪ್ಗೆ ಕಾರಣವಾಗುತ್ತದೆ.
- ನಿರ್ಧಾರಕತೆ: ಪರೀಕ್ಷೆಯನ್ನು ಪ್ರತಿ ಬಾರಿ ರನ್ ಮಾಡಿದಾಗಲೂ ಊಹಿಸಬಹುದಾದ ಡೇಟಾವನ್ನು ಹಿಂತಿರುಗಿಸಲು ನೀವು ಟೆಸ್ಟ್ ಡಬಲ್ ಅನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡಬಹುದು. ಇದು ಅಸ್ಥಿರ ಪರೀಕ್ಷೆಗಳನ್ನು ನಿವಾರಿಸುತ್ತದೆ ಮತ್ತು ವಿಫಲವಾದ ಪರೀಕ್ಷೆಯು ನಿಜವಾದ ಸಮಸ್ಯೆಯನ್ನು ಸೂಚಿಸುತ್ತದೆ ಎಂದು ಖಚಿತಪಡಿಸುತ್ತದೆ.
- ಎಡ್ಜ್ ಕೇಸ್ಗಳನ್ನು ಪರೀಕ್ಷಿಸುವ ಸಾಮರ್ಥ್ಯ:
ConnectionErrorಅನ್ನು ಹೆಚ್ಚಿಸುವುದು ಅಥವಾ ಖಾಲಿ ಡೇಟಾವನ್ನು ಹಿಂತಿರುಗಿಸುವಂತಹ ದೋಷ ಪರಿಸ್ಥಿತಿಗಳನ್ನು ಅನುಕರಿಸಲು ನೀವು ಡಬಲ್ ಅನ್ನು ಸುಲಭವಾಗಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಬಹುದು, ನಿಮ್ಮ ಕೋಡ್ ಈ ಪರಿಸ್ಥಿತಿಗಳನ್ನು ಸೊಗಸಾಗಿ ನಿರ್ವಹಿಸುತ್ತದೆ ಎಂದು ಪರಿಶೀಲಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ.
ಟೆಸ್ಟ್ ಡಬಲ್ಗಳ ವರ್ಗೀಕರಣ: ಕೇವಲ "ಮಾಕ್ಗಳಿಂದ" ಆಚೆಗೆ
ಡೆವಲಪರ್ಗಳು ಸಾಮಾನ್ಯವಾಗಿ ಯಾವುದೇ ಟೆಸ್ಟ್ ಡಬಲ್ಗೆ "ಮಾಕ್" ಎಂಬ ಪದವನ್ನು ಸಾಮಾನ್ಯವಾಗಿ ಬಳಸಿದರೂ, ಜೆರಾರ್ಡ್ ಮೆಸ್ಜಾರೋಸ್ ತನ್ನ "xUnit Test Patterns" ಪುಸ್ತಕದಲ್ಲಿ ನೀಡಿದ ಹೆಚ್ಚು ನಿಖರವಾದ ಪರಿಭಾಷೆಯನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳುವುದು ಸಹಾಯಕವಾಗಿದೆ. ಈ ವ್ಯತ್ಯಾಸಗಳನ್ನು ತಿಳಿದುಕೊಳ್ಳುವುದರಿಂದ ನಿಮ್ಮ ಪರೀಕ್ಷೆಯಲ್ಲಿ ನೀವು ಏನನ್ನು ಸಾಧಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದೀರಿ ಎಂಬುದರ ಕುರಿತು ಹೆಚ್ಚು ಸ್ಪಷ್ಟವಾಗಿ ಯೋಚಿಸಲು ಸಹಾಯ ಮಾಡುತ್ತದೆ.
1. ಡಮ್ಮಿ
ಒಂದು ಡಮ್ಮಿ ಆಬ್ಜೆಕ್ಟ್ ಅತ್ಯಂತ ಸರಳವಾದ ಟೆಸ್ಟ್ ಡಬಲ್ ಆಗಿದೆ. ಇದನ್ನು ಪ್ಯಾರಾಮೀಟರ್ ಪಟ್ಟಿಯನ್ನು ತುಂಬಲು ಬಳಸಲಾಗುತ್ತದೆ ಆದರೆ ಎಂದಿಗೂ ನಿಜವಾಗಿ ಬಳಸಲಾಗುವುದಿಲ್ಲ. ಇದರ ವಿಧಾನಗಳನ್ನು ಸಾಮಾನ್ಯವಾಗಿ ಕರೆಯಲಾಗುವುದಿಲ್ಲ. ನೀವು ಒಂದು ವಿಧಾನಕ್ಕೆ ಆರ್ಗ್ಯುಮೆಂಟ್ ಒದಗಿಸಬೇಕಾದಾಗ ಡಮ್ಮಿಯನ್ನು ಬಳಸುತ್ತೀರಿ, ಆದರೆ ನಿರ್ದಿಷ್ಟ ಪರೀಕ್ಷೆಯ ಸಂದರ್ಭದಲ್ಲಿ ಆ ಆರ್ಗ್ಯುಮೆಂಟ್ನ ನಡವಳಿಕೆಯ ಬಗ್ಗೆ ನಿಮಗೆ ಚಿಂತೆಯಿರುವುದಿಲ್ಲ.
ಉದಾಹರಣೆ: ಒಂದು ಕಾರ್ಯಕ್ಕೆ 'ಲಾಗರ್' ಆಬ್ಜೆಕ್ಟ್ ಅಗತ್ಯವಿದ್ದರೆ ಆದರೆ ನಿಮ್ಮ ಪರೀಕ್ಷೆಯು ಏನನ್ನು ಲಾಗ್ ಮಾಡಲಾಗುತ್ತದೆ ಎಂಬುದರ ಬಗ್ಗೆ ಕಾಳಜಿ ವಹಿಸದಿದ್ದರೆ, ನೀವು ಡಮ್ಮಿ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಕಳುಹಿಸಬಹುದು.
2. ಫೇಕ್
ಒಂದು ಫೇಕ್ ಆಬ್ಜೆಕ್ಟ್ ಕಾರ್ಯನಿರ್ವಹಿಸುವ ಅನುಷ್ಠಾನವನ್ನು ಹೊಂದಿದೆ, ಆದರೆ ಇದು ಉತ್ಪಾದನಾ ಆಬ್ಜೆಕ್ಟ್ನ ಹೆಚ್ಚು ಸರಳವಾದ ಆವೃತ್ತಿಯಾಗಿದೆ. ಇದು ಬಾಹ್ಯ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಬಳಸುವುದಿಲ್ಲ ಮತ್ತು ಭಾರವಾದ ಅನುಷ್ಠಾನಕ್ಕೆ ಹಗುರವಾದ ಅನುಷ್ಠಾನವನ್ನು ಬದಲಿಸುತ್ತದೆ. ಕ್ಲಾಸಿಕ್ ಉದಾಹರಣೆಯೆಂದರೆ ನೈಜ ಡೇಟಾಬೇಸ್ ಸಂಪರ್ಕವನ್ನು ಬದಲಿಸುವ ಇನ್-ಮೆಮೊರಿ ಡೇಟಾಬೇಸ್. ಇದು ನಿಜವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ—ನೀವು ಅದಕ್ಕೆ ಡೇಟಾವನ್ನು ಸೇರಿಸಬಹುದು ಮತ್ತು ಅದರಿಂದ ಡೇಟಾವನ್ನು ಓದಬಹುದು—ಆದರೆ ಇದು ಕೇವಲ ಸರಳ ನಿಘಂಟು ಅಥವಾ ಪಟ್ಟಿಯಾಗಿದೆ.
3. ಸ್ಟಬ್
ಒಂದು ಸ್ಟಬ್ ಪರೀಕ್ಷೆಯ ಸಮಯದಲ್ಲಿ ಮಾಡಿದ ವಿಧಾನ ಕರೆಗಳಿಗೆ ಪೂರ್ವ-ಪ್ರೋಗ್ರಾಮ್ ಮಾಡಿದ, "ಕ್ಯಾನೆಡ್" ಉತ್ತರಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ. ಅವಲಂಬನೆಯಿಂದ ನಿರ್ದಿಷ್ಟ ಡೇಟಾವನ್ನು ನಿಮ್ಮ ಕೋಡ್ ಸ್ವೀಕರಿಸಬೇಕಾದಾಗ ಇದನ್ನು ಬಳಸಲಾಗುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ನೀವು api_client.get_user(user_id=123) ನಂತಹ ವಿಧಾನವನ್ನು ಯಾವಾಗಲೂ ನಿರ್ದಿಷ್ಟ ಬಳಕೆದಾರರ ನಿಘಂಟನ್ನು ಹಿಂತಿರುಗಿಸಲು ಸ್ಟಬ್ ಮಾಡಬಹುದು, ನಿಜವಾಗಿ API ಕರೆ ಮಾಡದೆಯೇ.
4. ಸ್ಪೈ
ಒಂದು ಸ್ಪೈ ಒಂದು ಸ್ಟಬ್ ಆಗಿದ್ದು, ಅದನ್ನು ಹೇಗೆ ಕರೆಯಲಾಗಿದೆ ಎಂಬುದರ ಕುರಿತು ಕೆಲವು ಮಾಹಿತಿಯನ್ನು ಸಹ ದಾಖಲಿಸುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಒಂದು ವಿಧಾನವನ್ನು ಎಷ್ಟು ಬಾರಿ ಕರೆಯಲಾಗಿದೆ ಅಥವಾ ಅದಕ್ಕೆ ಯಾವ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ ಎಂಬುದನ್ನು ಇದು ದಾಖಲಿಸಬಹುದು. ಇದು ನಿಮ್ಮ ಕೋಡ್ ಮತ್ತು ಅದರ ಅವಲಂಬನೆಯ ನಡುವಿನ ಸಂವಹನದ ಮೇಲೆ "ಒಳಗೆ ನೋಡಲು" ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ ಮತ್ತು ನಂತರ ಆ ಸಂವಹನದ ಬಗ್ಗೆ ದೃಢೀಕರಣಗಳನ್ನು ಮಾಡಬಹುದು.
5. ಮಾಕ್
ಒಂದು ಮಾಕ್ ಅತ್ಯಂತ 'ಅರಿವಿನ' ಟೆಸ್ಟ್ ಡಬಲ್ ಪ್ರಕಾರವಾಗಿದೆ. ಇದು ಯಾವ ವಿಧಾನಗಳನ್ನು ಕರೆಯಲಾಗುತ್ತದೆ, ಯಾವ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗೆ ಮತ್ತು ಯಾವ ಕ್ರಮದಲ್ಲಿ ಕರೆಯಲಾಗುತ್ತದೆ ಎಂಬ ನಿರೀಕ್ಷೆಗಳೊಂದಿಗೆ ಪೂರ್ವ-ಪ್ರೋಗ್ರಾಮ್ ಮಾಡಲಾದ ಒಂದು ಆಬ್ಜೆಕ್ಟ್ ಆಗಿದೆ. ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಬಳಸುವ ಪರೀಕ್ಷೆಯು ಪರೀಕ್ಷೆಯಲ್ಲಿರುವ ಕೋಡ್ ತಪ್ಪಾದ ಫಲಿತಾಂಶವನ್ನು ನೀಡಿದರೆ ಮಾತ್ರವಲ್ಲದೆ, ನಿಖರವಾಗಿ ನಿರೀಕ್ಷಿತ ರೀತಿಯಲ್ಲಿ ಮಾಕ್ನೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸದಿದ್ದರೂ ಸಹ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ. ನಡವಳಿಕೆ ಪರಿಶೀಲನೆಗೆ ಮಾಕ್ಗಳು ಉತ್ತಮವಾಗಿವೆ—ನಿರ್ದಿಷ್ಟ ಕ್ರಿಯೆಗಳ ಅನುಕ್ರಮವು ಸಂಭವಿಸಿದೆ ಎಂದು ಖಚಿತಪಡಿಸುತ್ತದೆ.
ಪೈಥಾನ್ನ unittest.mock ಲೈಬ್ರರಿಯು ಒಂದು ಶಕ್ತಿಶಾಲಿ ವರ್ಗವನ್ನು ಒದಗಿಸುತ್ತದೆ, ಅದನ್ನು ನೀವು ಹೇಗೆ ಬಳಸುತ್ತೀರಿ ಎಂಬುದರ ಆಧಾರದ ಮೇಲೆ ಸ್ಟಬ್, ಸ್ಪೈ ಅಥವಾ ಮಾಕ್ ಆಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ.
ಪೈಥಾನ್ನ ಪವರ್ಹೌಸ್ ಅನ್ನು ಪರಿಚಯಿಸಲಾಗುತ್ತಿದೆ: `unittest.mock` ಲೈಬ್ರರಿ
ಪೈಥಾನ್ನ ಪ್ರಮಾಣಿತ ಲೈಬ್ರರಿಯ ಭಾಗವಾಗಿ 3.3 ಆವೃತ್ತಿಯಿಂದ, unittest.mock ಟೆಸ್ಟ್ ಡಬಲ್ಗಳನ್ನು ರಚಿಸಲು ಒಂದು ಕ್ಯಾನೋನಿಕಲ್ ಪರಿಹಾರವಾಗಿದೆ. ಇದರ ನಮ್ಯತೆ ಮತ್ತು ಶಕ್ತಿಯು ಯಾವುದೇ ಗಂಭೀರ ಪೈಥಾನ್ ಡೆವಲಪರ್ಗೆ ಇದು ಅತ್ಯಗತ್ಯ ಸಾಧನವಾಗಿದೆ. ನೀವು ಪೈಥಾನ್ನ ಹಳೆಯ ಆವೃತ್ತಿಯನ್ನು ಬಳಸುತ್ತಿದ್ದರೆ, ನೀವು ಪಿಪ್ ಮೂಲಕ ಬ್ಯಾಕ್ಪೋರ್ಟೆಡ್ ಲೈಬ್ರರಿಯನ್ನು ಸ್ಥಾಪಿಸಬಹುದು: pip install mock.
ಲೈಬ್ರರಿಯ ಮೂಲವು ಎರಡು ಪ್ರಮುಖ ವರ್ಗಗಳ ಸುತ್ತ ಸುತ್ತುತ್ತದೆ: Mock ಮತ್ತು ಅದರ ಹೆಚ್ಚು ಸಾಮರ್ಥ್ಯವಿರುವ ಸಹೋದರಿ, MagicMock. ಈ ಆಬ್ಜೆಕ್ಟ್ಗಳನ್ನು ನಂಬಲಾಗದಷ್ಟು ನಮ್ಯವಾಗಿ ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿದೆ, ನೀವು ಅವುಗಳನ್ನು ಪ್ರವೇಶಿಸುವಾಗ ತಕ್ಷಣವೇ ಗುಣಲಕ್ಷಣಗಳು ಮತ್ತು ವಿಧಾನಗಳನ್ನು ರಚಿಸುತ್ತವೆ.
ಆಳವಾದ ವಿಶ್ಲೇಷಣೆ: `Mock` ಮತ್ತು `MagicMock` ವರ್ಗಗಳು
`Mock` ಆಬ್ಜೆಕ್ಟ್
ಒಂದು `Mock` ಆಬ್ಜೆಕ್ಟ್ ಒಂದು ಊಸರವಳ್ಳಿ. ನೀವು ಒಂದನ್ನು ರಚಿಸಬಹುದು, ಮತ್ತು ಅದು ತಕ್ಷಣವೇ ಯಾವುದೇ ಗುಣಲಕ್ಷಣದ ಪ್ರವೇಶ ಅಥವಾ ವಿಧಾನ ಕರೆಯ ಪ್ರತಿಕ್ರಿಯಿಸುತ್ತದೆ, ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಮತ್ತೊಂದು ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಹಿಂದಿರುಗಿಸುತ್ತದೆ. ಇದು ಸೆಟಪ್ ಸಮಯದಲ್ಲಿ ಕರೆಗಳನ್ನು ಸುಲಭವಾಗಿ ಒಟ್ಟಿಗೆ ಜೋಡಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ.
# In a test file...
from unittest.mock import Mock
# Create a mock object
mock_api = Mock()
# Accessing an attribute creates it and returns another mock
print(mock_api.users)
# Output: <Mock name='mock.users' id='...'>
# Calling a method also returns a mock by default
print(mock_api.users.get(id=1))
# Output: <Mock name='mock.users.get()' id='...'>
ಈ ಪೂರ್ವನಿಯೋಜಿತ ನಡವಳಿಕೆಯು ಪರೀಕ್ಷೆಗೆ ಹೆಚ್ಚು ಉಪಯುಕ್ತವಲ್ಲ. ನೈಜ ಶಕ್ತಿಯು ಮಾಕ್ ಅನ್ನು ಅದು ಬದಲಾಯಿಸುತ್ತಿರುವ ಆಬ್ಜೆಕ್ಟ್ನಂತೆ ವರ್ತಿಸಲು ಕಾನ್ಫಿಗರ್ ಮಾಡುವುದರಿಂದ ಬರುತ್ತದೆ.
ಹಿಂತಿರುಗಿಸುವ ಮೌಲ್ಯಗಳು ಮತ್ತು ಅಡ್ಡ ಪರಿಣಾಮಗಳನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡುವುದು
return_value ಗುಣಲಕ್ಷಣವನ್ನು ಬಳಸಿಕೊಂಡು ಒಂದು ಮಾಕ್ ವಿಧಾನವು ಏನನ್ನು ಹಿಂದಿರುಗಿಸಬೇಕು ಎಂದು ನೀವು ಹೇಳಬಹುದು. ನೀವು Stub ಅನ್ನು ರಚಿಸುವುದು ಹೀಗೆ.
from unittest.mock import Mock
# Create a mock for a data service
mock_service = Mock()
# Configure the return value for a method call
mock_service.get_data.return_value = {'id': 1, 'name': 'Test Data'}
# Now when we call it, we get our configured value
result = mock_service.get_data()
print(result)
# Output: {'id': 1, 'name': 'Test Data'}
ದೋಷಗಳನ್ನು ಅನುಕರಿಸಲು, ನೀವು side_effect ಗುಣಲಕ್ಷಣವನ್ನು ಬಳಸಬಹುದು. ನಿಮ್ಮ ಕೋಡ್ನ ದೋಷ ನಿರ್ವಹಣೆಯನ್ನು ಪರೀಕ್ಷಿಸಲು ಇದು ಸೂಕ್ತವಾಗಿದೆ.
from unittest.mock import Mock
mock_service = Mock()
# Configure the method to raise an exception
mock_service.get_data.side_effect = ConnectionError("Failed to connect to service")
# Calling the method will now raise the exception
try:
mock_service.get_data()
except ConnectionError as e:
print(e)
# Output: Failed to connect to service
ಪರಿಶೀಲನೆಗಾಗಿ ಅಸರ್ಷನ್ ವಿಧಾನಗಳು
ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ಗಳು ಸ್ಪೈಸ್ ಮತ್ತು ಮಾಕ್ಗಳಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತವೆ, ಅವುಗಳನ್ನು ಹೇಗೆ ಬಳಸಲಾಗುತ್ತದೆ ಎಂಬುದನ್ನು ದಾಖಲಿಸುತ್ತವೆ. ನಂತರ ನೀವು ಈ ಸಂವಹನಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಅಂತರ್ನಿರ್ಮಿತ ಅಸರ್ಷನ್ ವಿಧಾನಗಳ ಸೂಟ್ ಅನ್ನು ಬಳಸಬಹುದು.
mock_object.method.assert_called(): ವಿಧಾನವನ್ನು ಕನಿಷ್ಠ ಒಂದು ಬಾರಿಯಾದರೂ ಕರೆಯಲಾಗಿದೆ ಎಂದು ದೃಢಪಡಿಸುತ್ತದೆ.mock_object.method.assert_called_once(): ವಿಧಾನವನ್ನು ನಿಖರವಾಗಿ ಒಂದು ಬಾರಿ ಕರೆಯಲಾಗಿದೆ ಎಂದು ದೃಢಪಡಿಸುತ್ತದೆ.mock_object.method.assert_called_with(*args, **kwargs): ವಿಧಾನವನ್ನು ನಿರ್ದಿಷ್ಟ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗೆ ಕೊನೆಯದಾಗಿ ಕರೆಯಲಾಗಿದೆ ಎಂದು ದೃಢಪಡಿಸುತ್ತದೆ.mock_object.method.assert_any_call(*args, **kwargs): ವಿಧಾನವನ್ನು ಯಾವುದೇ ಹಂತದಲ್ಲಿ ಈ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗೆ ಕರೆಯಲಾಗಿದೆ ಎಂದು ದೃಢಪಡಿಸುತ್ತದೆ.mock_object.method.assert_not_called(): ವಿಧಾನವನ್ನು ಎಂದಿಗೂ ಕರೆಯಲಾಗಿಲ್ಲ ಎಂದು ದೃಢಪಡಿಸುತ್ತದೆ.mock_object.call_count: ವಿಧಾನವನ್ನು ಎಷ್ಟು ಬಾರಿ ಕರೆಯಲಾಗಿದೆ ಎಂದು ಹೇಳುವ ಪೂರ್ಣಾಂಕದ ಗುಣಲಕ್ಷಣ.
from unittest.mock import Mock
mock_notifier = Mock()
# Imagine this is our function under test
def process_and_notify(data, notifier):
if data.get('critical'):
notifier.send_alert(message="Critical event occurred!")
# Test case 1: Critical data
process_and_notify({'critical': True}, mock_notifier)
mock_notifier.send_alert.assert_called_once_with(message="Critical event occurred!")
# Reset the mock for the next test
mock_notifier.reset_mock()
# Test case 2: Non-critical data
process_and_notify({'critical': False}, mock_notifier)
mock_notifier.send_alert.assert_not_called()
`MagicMock` ಆಬ್ಜೆಕ್ಟ್
ಒಂದು `MagicMock` `Mock` ನ ಉಪವರ್ಗವಾಗಿದೆ, ಪ್ರಮುಖ ವ್ಯತ್ಯಾಸದೊಂದಿಗೆ: ಇದು ಪೈಥಾನ್ನ ಹೆಚ್ಚಿನ "ಮ್ಯಾಜಿಕ್" ಅಥವಾ "ಡಂಡರ್" ವಿಧಾನಗಳಿಗೆ (ಉದಾ., __len__, __str__, __iter__) ಪೂರ್ವನಿಯೋಜಿತ ಅನುಷ್ಠಾನಗಳನ್ನು ಹೊಂದಿದೆ. ಈ ವಿಧಾನಗಳಲ್ಲಿ ಒಂದನ್ನು ಅಗತ್ಯವಿರುವ ಸಂದರ್ಭದಲ್ಲಿ ನೀವು ಸಾಮಾನ್ಯ `Mock` ಅನ್ನು ಬಳಸಲು ಪ್ರಯತ್ನಿಸಿದರೆ, ನಿಮಗೆ ದೋಷ ಸಿಗುತ್ತದೆ.
from unittest.mock import Mock, MagicMock
# Using a regular Mock
mock_list = Mock()
try:
len(mock_list)
except TypeError as e:
print(e) # Output: 'Mock' object has no len()
# Using a MagicMock
magic_mock_list = MagicMock()
print(len(magic_mock_list)) # Output: 0 (by default)
# We can configure the magic method's return value too
magic_mock_list.__len__.return_value = 100
print(len(magic_mock_list)) # Output: 100
ಅಂಗುಲಿ ನಿರ್ದೇಶನ: `MagicMock` ನಿಂದ ಪ್ರಾರಂಭಿಸಿ. ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸುರಕ್ಷಿತವಾಗಿದೆ ಮತ್ತು for ಲೂಪ್ಗಳಲ್ಲಿ (__iter__ ಅಗತ್ಯವಿದೆ) ಅಥವಾ with ಹೇಳಿಕೆಗಳಲ್ಲಿ (__enter__ ಮತ್ತು __exit__ ಅಗತ್ಯವಿದೆ) ಬಳಸಲಾಗುವ ಆಬ್ಜೆಕ್ಟ್ಗಳನ್ನು ಮಾಕ್ ಮಾಡುವಂತಹ ಹೆಚ್ಚಿನ ಬಳಕೆಯ ಸಂದರ್ಭಗಳನ್ನು ಒಳಗೊಂಡಿದೆ.
ಪ್ರಾಯೋಗಿಕ ಅನುಷ್ಠಾನ: `patch` ಡೆಕೋರೇಟರ್ ಮತ್ತು ಕಾನ್ಟೆಕ್ಸ್ಟ್ ಮ್ಯಾನೇಜರ್
ಮಾಕ್ ಅನ್ನು ರಚಿಸುವುದು ಒಂದು ವಿಷಯ, ಆದರೆ ನೈಜ ಆಬ್ಜೆಕ್ಟ್ಗೆ ಬದಲಾಗಿ ಅದನ್ನು ಬಳಸಲು ನಿಮ್ಮ ಕೋಡ್ ಅನ್ನು ಹೇಗೆ ಮಾಡುವುದು? ಇಲ್ಲಿ `patch` ಬರುತ್ತದೆ. `patch` ಎಂಬುದು `unittest.mock` ನಲ್ಲಿರುವ ಒಂದು ಶಕ್ತಿಶಾಲಿ ಸಾಧನವಾಗಿದ್ದು, ಪರೀಕ್ಷೆಯ ಅವಧಿಗೆ ಒಂದು ಗುರಿ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಮಾಕ್ನೊಂದಿಗೆ ತಾತ್ಕಾಲಿಕವಾಗಿ ಬದಲಾಯಿಸುತ್ತದೆ.
ಡೆಕೋರೇಟರ್ ಆಗಿ `@patch`
`patch` ಅನ್ನು ಬಳಸಲು ಸಾಮಾನ್ಯ ಮಾರ್ಗವೆಂದರೆ ನಿಮ್ಮ ಪರೀಕ್ಷಾ ವಿಧಾನದ ಮೇಲೆ ಡೆಕೋರೇಟರ್ ಆಗಿ. ನೀವು ಬದಲಾಯಿಸಲು ಬಯಸುವ ಆಬ್ಜೆಕ್ಟ್ಗೆ ಸ್ಟ್ರಿಂಗ್ ಪಾಥ್ ಅನ್ನು ಒದಗಿಸುತ್ತೀರಿ.
ಜನಪ್ರಿಯ `requests` ಲೈಬ್ರರಿಯನ್ನು ಬಳಸಿಕೊಂಡು ವೆಬ್ API ನಿಂದ ಡೇಟಾವನ್ನು ಪಡೆಯುವ ಕಾರ್ಯವನ್ನು ನಾವು ಹೊಂದಿದ್ದೇವೆ ಎಂದು ಹೇಳೋಣ:
# in file: my_app/data_fetcher.py
import requests
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status() # Raise an exception for bad status codes
return response.json()
ನೈಜ ನೆಟ್ವರ್ಕ್ ಕರೆ ಮಾಡದೆಯೇ ಈ ಕಾರ್ಯವನ್ನು ಪರೀಕ್ಷಿಸಲು ನಾವು ಬಯಸುತ್ತೇವೆ. ನಾವು `requests.get` ಅನ್ನು ಪ್ಯಾಚ್ ಮಾಡಬಹುದು:
# in file: tests/test_data_fetcher.py
import unittest
from unittest.mock import patch, Mock
from my_app.data_fetcher import get_user_data
class TestDataFetcher(unittest.TestCase):
@patch('my_app.data_fetcher.requests.get')
def test_get_user_data_success(self, mock_get):
"""Test successful data fetching."""
# Configure the mock to simulate a successful API response
mock_response = Mock()
mock_response.json.return_value = {'id': 1, 'name': 'John Doe'}
mock_response.raise_for_status.return_value = None # Do nothing on success
mock_get.return_value = mock_response
# Call our function
user_data = get_user_data(1)
# Assert our function made the correct API call
mock_get.assert_called_once_with('https://api.example.com/users/1')
# Assert our function returned the expected data
self.assertEqual(user_data, {'id': 1, 'name': 'John Doe'})
`patch` ಹೇಗೆ `MagicMock` ಅನ್ನು ರಚಿಸುತ್ತದೆ ಮತ್ತು ಅದನ್ನು `mock_get` ಆರ್ಗ್ಯುಮೆಂಟ್ ಆಗಿ ನಮ್ಮ ಪರೀಕ್ಷಾ ವಿಧಾನಕ್ಕೆ ಕಳುಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಗಮನಿಸಿ. ಪರೀಕ್ಷೆಯೊಳಗೆ, `my_app.data_fetcher` ಒಳಗೆ `requests.get` ಗೆ ಮಾಡಿದ ಯಾವುದೇ ಕರೆಯು ನಮ್ಮ ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ಗೆ ಮರುನಿರ್ದೇಶಿಸಲ್ಪಡುತ್ತದೆ.
ಕಾನ್ಟೆಕ್ಸ್ಟ್ ಮ್ಯಾನೇಜರ್ ಆಗಿ `patch`
ಕೆಲವೊಮ್ಮೆ ನಿಮಗೆ ಪರೀಕ್ಷೆಯ ಒಂದು ಸಣ್ಣ ಭಾಗಕ್ಕೆ ಮಾತ್ರ ಏನನ್ನಾದರೂ ಪ್ಯಾಚ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ. `with` ಹೇಳಿಕೆಯೊಂದಿಗೆ `patch` ಅನ್ನು ಕಾನ್ಟೆಕ್ಸ್ಟ್ ಮ್ಯಾನೇಜರ್ ಆಗಿ ಬಳಸುವುದು ಇದಕ್ಕೆ ಸೂಕ್ತವಾಗಿದೆ.
# in file: tests/test_data_fetcher.py
import unittest
from unittest.mock import patch, Mock
from my_app.data_fetcher import get_user_data
class TestDataFetcher(unittest.TestCase):
def test_get_user_data_with_context_manager(self):
"""Test using patch as a context manager."""
with patch('my_app.data_fetcher.requests.get') as mock_get:
# Configure the mock inside the 'with' block
mock_response = Mock()
mock_response.json.return_value = {'id': 2, 'name': 'Jane Doe'}
mock_get.return_value = mock_response
user_data = get_user_data(2)
mock_get.assert_called_once_with('https://api.example.com/users/2')
self.assertEqual(user_data, {'id': 2, 'name': 'Jane Doe'})
# Outside the 'with' block, requests.get is back to its original state
ಒಂದು ನಿರ್ಣಾಯಕ ಪರಿಕಲ್ಪನೆ: ಎಲ್ಲಿ ಪ್ಯಾಚ್ ಮಾಡಬೇಕು?
`patch` ಅನ್ನು ಬಳಸುವಾಗ ಇದು ಸಾಮಾನ್ಯ ಗೊಂದಲದ ಮೂಲವಾಗಿದೆ. ನಿಯಮ ಹೀಗಿದೆ: ನೀವು ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಎಲ್ಲಿ ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿದೆಯೋ ಅಲ್ಲಿ ಅಲ್ಲ, ಅದನ್ನು ಎಲ್ಲಿ ಹುಡುಕಲಾಗುತ್ತದೆಯೋ ಅಲ್ಲಿ ಪ್ಯಾಚ್ ಮಾಡಬೇಕು.
ಒಂದು ಉದಾಹರಣೆಯೊಂದಿಗೆ ವಿವರಿಸೋಣ. ನಾವು ಎರಡು ಫೈಲ್ಗಳನ್ನು ಹೊಂದಿದ್ದೇವೆ ಎಂದು ಭಾವಿಸೋಣ:
# in file: services.py
class Database:
def connect(self):
# ... complex connection logic ...
return "REAL_CONNECTION"
# in file: main_app.py
from services import Database
def start_app():
db = Database()
connection = db.connect()
print(f"Got connection: {connection}")
return connection
ಈಗ, ನಾವು ನಿಜವಾದ `Database` ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ರಚಿಸದೆಯೇ `main_app.py` ನಲ್ಲಿ `start_app` ಅನ್ನು ಪರೀಕ್ಷಿಸಲು ಬಯಸುತ್ತೇವೆ. `services.Database` ಅನ್ನು ಪ್ಯಾಚ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸುವುದು ಸಾಮಾನ್ಯ ತಪ್ಪು.
# in file: test_main_app.py
import unittest
from unittest.mock import patch
from main_app import start_app
class TestApp(unittest.TestCase):
# THIS IS THE WRONG WAY TO PATCH!
@patch('services.Database')
def test_start_app_incorrectly(self, mock_db):
start_app()
# This test will still use the REAL Database class!
# THIS IS THE CORRECT WAY TO PATCH!
@patch('main_app.Database')
def test_start_app_correctly(self, mock_db_class):
# We are patching 'Database' in the 'main_app' namespace
# Configure the mock instance that will be created
mock_instance = mock_db_class.return_value
mock_instance.connect.return_value = "MOCKED_CONNECTION"
connection = start_app()
# Assert that our mock was used
mock_db_class.assert_called_once() # Was the class instantiated?
mock_instance.connect.assert_called_once() # Was the connect method called?
self.assertEqual(connection, "MOCKED_CONNECTION")
ಮೊದಲ ಪರೀಕ್ಷೆ ಏಕೆ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ? ಏಕೆಂದರೆ `main_app.py` `from services import Database` ಅನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸುತ್ತದೆ. ಇದು `Database` ವರ್ಗವನ್ನು `main_app` ಮಾಡ್ಯೂಲ್ನ ನೇಮ್ಸ್ಪೇಸ್ಗೆ ಆಮದು ಮಾಡುತ್ತದೆ. `start_app` ರನ್ ಆದಾಗ, ಅದು ತನ್ನದೇ ಆದ ಮಾಡ್ಯೂಲ್ನಲ್ಲಿ (`main_app`) `Database` ಗಾಗಿ ಹುಡುಕುತ್ತದೆ. `services.Database` ಅನ್ನು ಪ್ಯಾಚ್ ಮಾಡುವುದರಿಂದ `services` ಮಾಡ್ಯೂಲ್ನಲ್ಲಿ ಬದಲಾಗುತ್ತದೆ, ಆದರೆ `main_app` ಈಗಾಗಲೇ ಮೂಲ ವರ್ಗಕ್ಕೆ ತನ್ನದೇ ಆದ ಉಲ್ಲೇಖವನ್ನು ಹೊಂದಿದೆ. ಸರಿಯಾದ ವಿಧಾನವೆಂದರೆ `main_app.Database` ಅನ್ನು ಪ್ಯಾಚ್ ಮಾಡುವುದು, ಇದು ಪರೀಕ್ಷೆಯಲ್ಲಿರುವ ಕೋಡ್ ವಾಸ್ತವವಾಗಿ ಬಳಸುವ ಹೆಸರು.
ಸುಧಾರಿತ ಮಾಕಿಂಗ್ ತಂತ್ರಗಳು
`spec` ಮತ್ತು `autospec`: ಮಾಕ್ಗಳನ್ನು ಸುರಕ್ಷಿತಗೊಳಿಸುವುದು
ಒಂದು ಪ್ರಮಾಣಿತ `MagicMock` ಸಂಭಾವ್ಯ ತೊಂದರೆಯನ್ನು ಹೊಂದಿದೆ: ಅದು ನಿಮಗೆ ಯಾವುದೇ ವಿಧಾನವನ್ನು ಯಾವುದೇ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳೊಂದಿಗೆ ಕರೆಯಲು ಅನುಮತಿಸುತ್ತದೆ, ಆ ವಿಧಾನವು ನೈಜ ಆಬ್ಜೆಕ್ಟ್ನಲ್ಲಿ ಇಲ್ಲದಿದ್ದರೂ ಸಹ. ಇದು ಪಾಸಾಗುವ ಪರೀಕ್ಷೆಗಳಿಗೆ ಕಾರಣವಾಗಬಹುದು ಆದರೆ ನೈಜ ಸಮಸ್ಯೆಗಳನ್ನು ಮರೆಮಾಡಬಹುದು, ಉದಾಹರಣೆಗೆ ವಿಧಾನದ ಹೆಸರುಗಳಲ್ಲಿನ ಟೈಪೋಗಳು ಅಥವಾ ನೈಜ ಆಬ್ಜೆಕ್ಟ್ನ API ನಲ್ಲಿನ ಬದಲಾವಣೆಗಳು.
# Real class
class Notifier:
def send_message(self, text):
# ... sends message ...
pass
# A test with a typo
from unittest.mock import MagicMock
mock_notifier = MagicMock()
# Oops, a typo! The real method is send_message
mock_notifier.send_mesage("hello") # No error is raised!
mock_notifier.send_mesage.assert_called_with("hello") # This assertion passes!
# Our test is green, but the production code would fail.
ಇದನ್ನು ತಡೆಯಲು, `unittest.mock` `spec` ಮತ್ತು `autospec` ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ.
- `spec=SomeClass`: ಇದು ಮಾಕ್ ಅನ್ನು `SomeClass` ನಂತೆಯೇ ಅದೇ API ಹೊಂದಲು ಕಾನ್ಫಿಗರ್ ಮಾಡುತ್ತದೆ. ನೈಜ ವರ್ಗದಲ್ಲಿ ಇಲ್ಲದ ವಿಧಾನ ಅಥವಾ ಗುಣಲಕ್ಷಣವನ್ನು ನೀವು ಪ್ರವೇಶಿಸಲು ಪ್ರಯತ್ನಿಸಿದರೆ, ಒಂದು `AttributeError` ಅನ್ನು ಹೆಚ್ಚಿಸಲಾಗುತ್ತದೆ.
- `autospec=True` (ಅಥವಾ `autospec=SomeClass`): ಇದು ಇನ್ನಷ್ಟು ಶಕ್ತಿಶಾಲಿಯಾಗಿದೆ. ಇದು `spec` ನಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ, ಆದರೆ ಇದು ಯಾವುದೇ ಮಾಕ್ ಮಾಡಿದ ವಿಧಾನಗಳ ಕರೆ ಸಹಿಯನ್ನು ಸಹ ಪರಿಶೀಲಿಸುತ್ತದೆ. ನೀವು ತಪ್ಪಾದ ಸಂಖ್ಯೆಯ ಅಥವಾ ಆರ್ಗ್ಯುಮೆಂಟ್ಗಳ ಹೆಸರುಗಳೊಂದಿಗೆ ವಿಧಾನವನ್ನು ಕರೆದರೆ, ಅದು `TypeError` ಅನ್ನು ಹೆಚ್ಚಿಸುತ್ತದೆ, ನೈಜ ಆಬ್ಜೆಕ್ಟ್ನಂತೆಯೇ.
from unittest.mock import create_autospec
# Create a mock that has the same interface as our Notifier class
spec_notifier = create_autospec(Notifier)
try:
# This will fail immediately because of the typo
spec_notifier.send_mesage("hello")
except AttributeError as e:
print(e) # Output: Mock object has no attribute 'send_mesage'
try:
# This will fail because the signature is wrong (no 'text' keyword)
spec_notifier.send_message("hello")
except TypeError as e:
print(e) # Output: missing a required argument: 'text'
# This is the correct way to call it
spec_notifier.send_message(text="hello") # This works!
spec_notifier.send_message.assert_called_once_with(text="hello")
ಉತ್ತಮ ಅಭ್ಯಾಸ: ಪ್ಯಾಚ್ ಮಾಡುವಾಗ ಯಾವಾಗಲೂ `autospec=True` ಅನ್ನು ಬಳಸಿ. ಇದು ನಿಮ್ಮ ಪರೀಕ್ಷೆಗಳನ್ನು ಹೆಚ್ಚು ದೃಢವಾಗಿಸುತ್ತದೆ ಮತ್ತು ಕಡಿಮೆ ದುರ್ಬಲಗೊಳಿಸುತ್ತದೆ. `@patch('path.to.thing', autospec=True)`.
ನೈಜ-ಪ್ರಪಂಚದ ಉದಾಹರಣೆ: ಡೇಟಾ ಪ್ರೊಸೆಸಿಂಗ್ ಸೇವೆಯನ್ನು ಪರೀಕ್ಷಿಸುವುದು
ಹೆಚ್ಚು ಸಂಪೂರ್ಣ ಉದಾಹರಣೆಯೊಂದಿಗೆ ಎಲ್ಲವನ್ನೂ ಒಟ್ಟಾಗಿ ಕಟ್ಟೋಣ. ನಾವು ಡೇಟಾಬೇಸ್ ಮತ್ತು ಫೈಲ್ ಸಿಸ್ಟಮ್ ಅನ್ನು ಅವಲಂಬಿಸಿರುವ `ReportGenerator` ಅನ್ನು ಹೊಂದಿದ್ದೇವೆ.
# in file: app/services.py
class DatabaseConnector:
def get_sales_data(self, start_date, end_date):
# In reality, this would query a database
raise NotImplementedError("This should not be called in tests")
class FileSaver:
def save_report(self, path, content):
# In reality, this would write to a file
raise NotImplementedError("This should not be called in tests")
# in file: app/reports.py
from .services import DatabaseConnector, FileSaver
class ReportGenerator:
def __init__(self):
self.db_connector = DatabaseConnector()
self.file_saver = FileSaver()
def generate_sales_report(self, start_date, end_date, output_path):
"""Fetches sales data and saves a formatted report."""
raw_data = self.db_connector.get_sales_data(start_date, end_date)
if not raw_data:
report_content = "No sales data for this period."
else:
total_sales = sum(item['amount'] for item in raw_data)
report_content = f"Total Sales from {start_date} to {end_date}: ${total_sales:.2f}"
self.file_saver.save_report(path=output_path, content=report_content)
return True
ಈಗ, `ReportGenerator.generate_sales_report` ಗಾಗಿ ಯೂನಿಟ್ ಪರೀಕ್ಷೆಯನ್ನು ಬರೆಯೋಣ, ಅದು ಅದರ ಅವಲಂಬನೆಗಳನ್ನು ಮಾಕ್ ಮಾಡುತ್ತದೆ.
# in file: tests/test_reports.py
import unittest
from datetime import date
from unittest.mock import patch, Mock
from app.reports import ReportGenerator
class TestReportGenerator(unittest.TestCase):
@patch('app.reports.FileSaver', autospec=True)
@patch('app.reports.DatabaseConnector', autospec=True)
def test_generate_sales_report_with_data(self, mock_db_connector_class, mock_file_saver_class):
"""Test report generation when the database returns data."""
# Arrange: Setup our mocks
mock_db_instance = mock_db_connector_class.return_value
mock_file_saver_instance = mock_file_saver_class.return_value
# Configure the database mock to return some fake data (Stub)
fake_data = [
{'id': 1, 'amount': 100.50},
{'id': 2, 'amount': 75.00},
{'id': 3, 'amount': 25.25}
]
mock_db_instance.get_sales_data.return_value = fake_data
start = date(2023, 1, 1)
end = date(2023, 1, 31)
path = '/reports/sales_jan_2023.txt'
# Act: Create an instance of our class and call the method
generator = ReportGenerator()
result = generator.generate_sales_report(start, end, path)
# Assert: Verify the interactions and results
# 1. Was the database called correctly?
mock_db_instance.get_sales_data.assert_called_once_with(start, end)
# 2. Was the file saver called with the correct, calculated content?
expected_content = "Total Sales from 2023-01-01 to 2023-01-31: $200.75"
mock_file_saver_instance.save_report.assert_called_once_with(
path=path,
content=expected_content
)
# 3. Did our method return the correct value?
self.assertTrue(result)
ಈ ಪರೀಕ್ಷೆಯು `generate_sales_report` ಒಳಗಿರುವ ತರ್ಕವನ್ನು ಡೇಟಾಬೇಸ್ ಮತ್ತು ಫೈಲ್ ಸಿಸ್ಟಮ್ನ ಸಂಕೀರ್ಣತೆಗಳಿಂದ ಸಂಪೂರ್ಣವಾಗಿ ಪ್ರತ್ಯೇಕಿಸುತ್ತದೆ, ಅದೇ ಸಮಯದಲ್ಲಿ ಅದು ಅವುಗಳೊಂದಿಗೆ ಸರಿಯಾಗಿ ಸಂವಹನ ನಡೆಸುತ್ತದೆ ಎಂದು ಪರಿಶೀಲಿಸುತ್ತದೆ.
ಪರಿಣಾಮಕಾರಿ ಮಾಕಿಂಗ್ಗಾಗಿ ಉತ್ತಮ ಅಭ್ಯಾಸಗಳು
- ಮಾಕ್ಗಳನ್ನು ಸರಳವಾಗಿ ಇರಿಸಿ: ಒಂದು ಅತ್ಯಂತ ಸಂಕೀರ್ಣವಾದ ಮಾಕ್ ಕಾನ್ಫಿಗರೇಶನ್ ಅಗತ್ಯವಿರುವ ಪರೀಕ್ಷೆಯು ಸಾಮಾನ್ಯವಾಗಿ ಪರೀಕ್ಷೆಯಲ್ಲಿರುವ ಘಟಕವು ತುಂಬಾ ಸಂಕೀರ್ಣವಾಗಿದೆ ಮತ್ತು ಏಕ ಜವಾಬ್ದಾರಿ ತತ್ವವನ್ನು ಉಲ್ಲಂಘಿಸುತ್ತಿದೆ ಎಂಬುದಕ್ಕೆ ಒಂದು ಚಿಹ್ನೆ (ಒಂದು "ಪರೀಕ್ಷಾ ವಾಸನೆ"). ಉತ್ಪಾದನಾ ಕೋಡ್ ಅನ್ನು ಮರುಸಂರಚಿಸುವುದನ್ನು ಪರಿಗಣಿಸಿ.
- ಸಹಯೋಗಿಗಳನ್ನು ಮಾಕ್ ಮಾಡಿ, ಎಲ್ಲವನ್ನೂ ಅಲ್ಲ: ನಿಮ್ಮ ಪರೀಕ್ಷೆಯಲ್ಲಿರುವ ಘಟಕವು ಸಂವಹನ ಮಾಡುವ ಆಬ್ಜೆಕ್ಟ್ಗಳನ್ನು ಮಾತ್ರ ನೀವು ಮಾಕ್ ಮಾಡಬೇಕು (ಅದರ ಸಹಯೋಗಿಗಳು). ನೀವು ಪರೀಕ್ಷಿಸುತ್ತಿರುವ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಮಾಕ್ ಮಾಡಬೇಡಿ.
- `autospec=True` ಗೆ ಆದ್ಯತೆ ನೀಡಿ: ಮೊದಲೇ ಹೇಳಿದಂತೆ, ಇದು ಮಾಕ್ನ ಇಂಟರ್ಫೇಸ್ ನೈಜ ಆಬ್ಜೆಕ್ಟ್ನ ಇಂಟರ್ಫೇಸ್ಗೆ ಹೊಂದಿಕೆಯಾಗುತ್ತದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳುವ ಮೂಲಕ ನಿಮ್ಮ ಪರೀಕ್ಷೆಗಳನ್ನು ಹೆಚ್ಚು ದೃಢವಾಗಿಸುತ್ತದೆ ಮತ್ತು ಕಡಿಮೆ ದುರ್ಬಲಗೊಳಿಸುತ್ತದೆ. `@patch('path.to.thing', autospec=True)`.
- ಪ್ರತಿ ಪರೀಕ್ಷೆಗೆ ಒಂದು ಮಾಕ್ (ಆದರ್ಶಪ್ರಾಯವಾಗಿ): ಒಂದು ಉತ್ತಮ ಯೂನಿಟ್ ಪರೀಕ್ಷೆಯು ಒಂದೇ ನಡವಳಿಕೆ ಅಥವಾ ಸಂವಹನದ ಮೇಲೆ ಕೇಂದ್ರೀಕರಿಸುತ್ತದೆ. ಒಂದು ಪರೀಕ್ಷೆಯಲ್ಲಿ ಅನೇಕ ವಿಭಿನ್ನ ಆಬ್ಜೆಕ್ಟ್ಗಳನ್ನು ಮಾಕ್ ಮಾಡುತ್ತಿರುವುದನ್ನು ನೀವು ಕಂಡುಕೊಂಡರೆ, ಅದನ್ನು ಹಲವಾರು, ಹೆಚ್ಚು ಕೇಂದ್ರೀಕೃತ ಪರೀಕ್ಷೆಗಳಾಗಿ ವಿಭಜಿಸುವುದು ಉತ್ತಮ.
- ನಿಮ್ಮ ಅಸರ್ಷನ್ಗಳಲ್ಲಿ ನಿರ್ದಿಷ್ಟವಾಗಿರಿ: ಕೇವಲ `mock.method.assert_called()` ಅನ್ನು ಪರಿಶೀಲಿಸಬೇಡಿ. ಸರಿಯಾದ ಡೇಟಾದೊಂದಿಗೆ ಸಂವಹನ ಸಂಭವಿಸಿದೆ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು `assert_called_with(...)` ಅನ್ನು ಬಳಸಿ. ಇದು ನಿಮ್ಮ ಪರೀಕ್ಷೆಗಳನ್ನು ಹೆಚ್ಚು ಮೌಲ್ಯಯುತವಾಗಿಸುತ್ತದೆ.
- ನಿಮ್ಮ ಪರೀಕ್ಷೆಗಳು ದಸ್ತಾವೇಜನ್ನು: ನಿಮ್ಮ ಪರೀಕ್ಷೆಗಳು ಮತ್ತು ಮಾಕ್ ಆಬ್ಜೆಕ್ಟ್ಗಳಿಗೆ ಸ್ಪಷ್ಟ ಮತ್ತು ವಿವರಣಾತ್ಮಕ ಹೆಸರುಗಳನ್ನು ಬಳಸಿ (ಉದಾ., `mock_api_client`, `test_login_fails_on_network_error`). ಇದು ಇತರ ಡೆವಲಪರ್ಗಳಿಗೆ ಪರೀಕ್ಷೆಯ ಉದ್ದೇಶವನ್ನು ಸ್ಪಷ್ಟಪಡಿಸುತ್ತದೆ.
ತೀರ್ಮಾನ
ಟೆಸ್ಟ್ ಡಬಲ್ಗಳು ಕೇವಲ ಪರೀಕ್ಷೆಗಾಗಿ ಒಂದು ಸಾಧನವಲ್ಲ; ಅವು ಪರೀಕ್ಷಿಸಬಹುದಾದ, ಮಾಡ್ಯುಲರ್ ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದಾದ ಸಾಫ್ಟ್ವೇರ್ ಅನ್ನು ವಿನ್ಯಾಸಗೊಳಿಸುವ ಮೂಲಭೂತ ಭಾಗವಾಗಿದೆ. ನೈಜ ಅವಲಂಬನೆಗಳನ್ನು ನಿಯಂತ್ರಿತ ಪರ್ಯಾಯಗಳೊಂದಿಗೆ ಬದಲಾಯಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ನ ತರ್ಕದ ಪ್ರತಿಯೊಂದು ಮೂಲೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಮರ್ಥವಾಗಿರುವ, ವೇಗದ, ವಿಶ್ವಾಸಾರ್ಹ ಪರೀಕ್ಷಾ ಸೂಟ್ ಅನ್ನು ನೀವು ರಚಿಸಬಹುದು.
ಪೈಥಾನ್ನ unittest.mock ಲೈಬ್ರರಿಯು ಈ ಮಾದರಿಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸಲು ವಿಶ್ವ ದರ್ಜೆಯ ಟೂಲ್ಕಿಟ್ ಅನ್ನು ಒದಗಿಸುತ್ತದೆ. MagicMock, `patch` ಮತ್ತು `autospec` ನ ಸುರಕ್ಷತೆಯನ್ನು ಕರಗತ ಮಾಡಿಕೊಳ್ಳುವ ಮೂಲಕ, ನೀವು ನಿಜವಾಗಿಯೂ ಪ್ರತ್ಯೇಕವಾದ ಯೂನಿಟ್ ಪರೀಕ್ಷೆಗಳನ್ನು ಬರೆಯುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುತ್ತೀರಿ. ಇದು ಹಿಂಜರಿಕೆಗಳನ್ನು ಹಿಡಿಯಲು ಮತ್ತು ಹೊಸ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಮೌಲ್ಯೀಕರಿಸಲು ನಿಖರವಾದ, ಉದ್ದೇಶಿತ ಪರೀಕ್ಷೆಗಳ ಸುರಕ್ಷತಾ ಜಾಲವನ್ನು ನೀವು ಹೊಂದಿದ್ದೀರಿ ಎಂದು ತಿಳಿದುಕೊಂಡು, ಆತ್ಮವಿಶ್ವಾಸದಿಂದ ಸಂಕೀರ್ಣ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ಮಿಸಲು ನಿಮಗೆ ಅಧಿಕಾರ ನೀಡುತ್ತದೆ. ಆದ್ದರಿಂದ ಮುಂದುವರಿಯಿರಿ, ಪ್ಯಾಚ್ ಮಾಡಲು ಪ್ರಾರಂಭಿಸಿ ಮತ್ತು ಇಂದು ಹೆಚ್ಚು ದೃಢವಾದ ಪೈಥಾನ್ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ಮಿಸಿ.